﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Marmat.Foundation;
using OfficeOpenXml;

namespace Marmat.Template.WinForm
{
    /// <summary>
    /// 
    /// </summary>
    public partial class ExportForm : Marmat.Forms.Form, IExportForm
    {
        private Marmat.Foundation.IExport export;
        private string sheetName;
        private readonly object empty = new object();
        private readonly Pen brorderPen = new Pen(Color.FromArgb(229, 229, 229));
        private readonly List<string> collection = new List<string>();
        private readonly Dictionary<string, (string, Type)> sourceCollection = new Dictionary<string, (string, Type)>();
        private readonly Dictionary<object, Rectangle> collection_Rectangle = new Dictionary<object, Rectangle>();

        public ExportForm()
        {
            InitializeComponent();
        }

        public void initialization(Marmat.Foundation.IExport export)
        {
            this.export = export;
            var dataSource = export.Export();
            this.listBox1.Items.Clear();
            this.listBox1.Items.Add(empty);
            if (dataSource is System.Data.DataTable data)
            {
                foreach (System.Data.DataColumn column in data.Columns)
                {
                    if (export.FieldBuilder(column.ColumnName))
                        sourceCollection[column.ColumnName] = (column.Caption, column.DataType);
                }
                this.listBox1.Items.AddRange(sourceCollection.Keys);
            }
            else if (dataSource is System.Collections.IEnumerable enumerable)
            {
                Type sourceType = dataSource.GetType();
                if (sourceType.IsGenericType && sourceType.GenericTypeArguments.Length == 1)
                    sourceType = sourceType.GenericTypeArguments[0];
                else if (sourceType.IsArray && dataSource is Array array)
                    sourceType = sourceType.GetElementType();
                var members = sourceType.GetMembers().Where(x => x is FieldInfo or PropertyInfo);
                foreach (var member in members)
                {
                    if (export.FieldBuilder(member.Name))
                    {
                        string caption = member.Name;
                        Type type = typeof(object);
                        if (member is FieldInfo field)
                            type = field.FieldType;
                        else if (member is PropertyInfo property)
                            type = property.PropertyType;
                        var description = member.GetCustomAttribute<DescriptionAttribute>();
                        if (description is not null)
                            caption = description.Description;
                        sourceCollection[member.Name] = (caption, type);
                    }
                }
                this.listBox1.Items.AddRange(sourceCollection.Keys);
            }
        }

        /// <summary>
        /// 导出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Excel文件|*.xlsx"; //设置筛选器
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                object dataSource = null;
                string sheetName = this.sheetName ?? "data";
                if (this.radioButton1.Checked)
                    dataSource = export.Export();
                else if (this.radioButton2.Checked)
                    dataSource = export.ExportAll();

                ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
                using var stream = File.Create(dialog.FileName);
                using var excel = new ExcelPackage(stream);
                ExcelWorksheet sheet = excel.Workbook.Worksheets.Add(sheetName);

                int columnIndex = 0;
                int rowIndex = 0;

                // 生成标题区域
                var items = this.sourceCollection.Where(x => this.collection.Contains(x.Key));
                foreach (var item in items)
                {
                    string name = item.Key;
                    string description = item.Value.Item1;
                    Type type = item.Value.Item2;
                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>))
                        type = type.GenericTypeArguments[0];
                    sheet.Cells[rowIndex + 1, columnIndex + 1].Value = description;
                    var column = sheet.Column(columnIndex + 1);
                    if (type == typeof(DateTime))
                        column.Style.Numberformat.Format = "yyyy-mm-dd";
                    column.Width = 20;
                    columnIndex++;
                }

                // 第一行作为列标题，居中显示。
                sheet.Row(1).Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
                sheet.Row(1).Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
                sheet.View.FreezePanes(2, 1); //冻结首行

                // 生成内容区域
                columnIndex = 0;
                rowIndex = 1;
                if (dataSource is System.Data.DataTable data)
                {
                    foreach (DataRow row in data.Rows)
                    {
                        columnIndex = 0;
                        foreach (var item in items)
                        {
                            string name = item.Key;
                            sheet.Cells[rowIndex + 1, columnIndex + 1].Value = row[name];
                            columnIndex++;
                        }
                        rowIndex++;
                    }
                }
                else if (dataSource is System.Collections.IEnumerable enumerable)
                {
                    foreach (var row in enumerable)
                    {
                        columnIndex = 0;
                        Type type = row.GetType().GetTypeInfo();
                        var members = type.GetMembers();
                        foreach (var item in items)
                        {
                            string name = item.Key;
                            var member = members.FirstOrDefault(x => x.Name == name);
                            object value = null;
                            if (member is FieldInfo field)
                                value = field.GetValue(row);
                            else if (member is PropertyInfo property)
                                value = property.GetValue(row);
                            sheet.Cells[rowIndex + 1, columnIndex + 1].Value = value;
                            columnIndex++;
                        }
                        rowIndex++;
                    }
                }
                excel.Save();
                this.DialogResult = DialogResult.OK;
            }
        }


        bool IExportForm.ShowDialog(IExport export, string sheetName)
        {
            this.initialization(export);
            this.sheetName = sheetName;
            return this.ShowDialog() == DialogResult.OK;
        }

        private void listBox1_Paint(object sender, PaintEventArgs e)
        {
            collection_Rectangle.Clear();
        }

        private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
        {
            object item = this.listBox1.Items[e.Index];
            collection_Rectangle[item] = e.Bounds;
            string displayText = string.Empty;
            string columnName = item.ToString();
            if (item == this.empty)
                displayText = "(选择全部)";
            else
                displayText = sourceCollection[item.ToString()].Item1;
            using (SolidBrush brush = new SolidBrush(Color.White))
            {
                if (e.State == DrawItemState.HotLight)
                    brush.Color = Color.FromArgb(230, 247, 246);
                else if (e.State == DrawItemState.Selected)
                {
                    if (e.Bounds.Contains(this.listBox1.PointToClient(Control.MousePosition)))
                        brush.Color = Color.FromArgb(230, 247, 246);
                }
                e.Graphics.FillRectangle(brush, e.Bounds);
            }

            Image img;
            Color foreColor = Color.Black;
            if (item == this.empty)
            {
                foreColor = Color.Gray;
                if (this.collection.Count == 0)
                    img = Properties.Resources.check_default;
                else if (this.sourceCollection.Keys.FirstOrDefault(x => !this.collection.Contains(x)) is not null)
                    img = Properties.Resources.check_un;
                else
                    img = Properties.Resources.check_checked;
            }
            else if (this.collection.Contains(columnName))
                img = Properties.Resources.check_checked;
            else
                img = Properties.Resources.check_default;
            e.Graphics.DrawImage(img, e.Bounds.X + 12, e.Bounds.Y + (e.Bounds.Height - img.Height) / 2);

            Rectangle rectangle = new Rectangle(e.Bounds.X + 40, e.Bounds.Y, e.Bounds.Width - 40, e.Bounds.Height);
            TextRenderer.DrawText(e.Graphics, displayText, e.Font, rectangle, foreColor, TextFormatFlags.VerticalCenter);
        }

        private void listBox1_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                var item = collection_Rectangle.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                if (item is not null)
                {
                    if (item == this.empty)
                    {
                        if (this.sourceCollection.Keys.ToList().TrueForAll(x => this.collection.Contains(x)))
                            this.collection.Clear();
                        else
                            this.collection.AddRange(this.sourceCollection.Keys.Where(x => !this.collection.Contains(x)));
                    }
                    else
                    {
                        string columnName = item.ToString();
                        if (collection.Contains(columnName))
                            collection.Remove(columnName);
                        else
                            collection.Add(columnName);
                    }
                    this.listBox1.Invalidate();
                }
            }
        }

        private void panel2_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(brorderPen, 0, 0, this.panel2.Width, 0);
        }

        private void flowLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(brorderPen, 0, this.flowLayoutPanel1.Height - 1, this.flowLayoutPanel1.Width, this.flowLayoutPanel1.Height - 1);
        }
    }
}